package ngat.oss.simulation;

import java.util.*;
import ngat.phase2.*;
import ngat.util.*;
import ngat.util.logging.*;

public class BasicCandidateGenerator implements CandidateGenerator {
    
    /** Phase2 information.*/
    private Phase2Model phase2Model;
    
    /** Execution history.,*/
    private ExecutionHistoryModel  historyModel;
    
    /** Accounting information.*/
    private AccountingModel accModel;

    /** Execution timing and feasibility model.*/
    private ExecutionTimingModel execModel;
    
    /** Metric scoring.*/
    private ScoringModel scoringModel;

    /** Metric rank-selector.*/
    private SelectionHeuristic selector;

    /** Logger.*/
    private LogProxy logger;

    /** Create a  BasicCandidateGenerator.*/
    public BasicCandidateGenerator(Phase2Model             phase2Model,
				   ExecutionHistoryModel   historyModel,
				   AccountingModel         accModel,
				   ExecutionTimingModel    execModel,
				   ScoringModel            scoringModel) {
	this.phase2Model   = phase2Model;
	this.historyModel  = historyModel;
	this.accModel      = accModel;
	this.execModel     = execModel;
	this.scoringModel  = scoringModel;  

	Logger slogger = LogManager.getLogger("SIM");
	logger = new LogProxy("BCG", "", slogger);

    }
    

    /** Returns a ranked sequence of candidates at the specified time 
     * under the specified environmental conditions.
     * @param time The time we want the metrics for.
     * @param env An environement snapshot at that time.
     */
    public List getCandidateMetrics(long time, EnvironmentSnapshot env) throws Exception {

	List candidates = new Vector();
	
	// TODO should check if sun is up/down here as this will terminate here.

	Iterator ip = phase2Model.listProposals();
	while (ip.hasNext()) {

	    Proposal proposal = (Proposal)ip.next();

	    
	    // VETO: Proposal null.
	    if (proposal == null) continue;

	    logger.method("getCandidateMetrics(t,Env)").log(2, "Check proposal "+proposal.getName());

	    // VETO: Proposal outside activation period.        
	    if (proposal.getActivationDate() > time ||
		proposal.getExpiryDate() < time) {
		logger.log(2, "Proposal skipped: PROP_ACTIVATION");
		continue;
	    }
		
// 	    // VETO: Proposal has no time left.
// 	    if (proposal.getUsedTime() >= proposal.getAllocatedTime()) {
// 		logger.log(2, "Proposal skipped: PROP_TIME_ALLOC");
// 		continue;
// 	    }


	    // VETO: Proposal used up all allocated time.
	    Accounts acc = accModel.getAccounts(proposal.getFullPath());
	    if (acc == null) {
		logger.log(2, "Proposal skipped: PROP_NO_ACCOUNTS");
		continue;
	    }
	    
	    Account total = acc.getAccount(AccountingModel.ACCOUNT_TOTAL);
	    if (total == null) {
		logger.log(2, "Proposal skipped: PROP_NO_TIME_ALLOC");
		continue;
	    }	
	    
	    if (total.getUsed() >= total.getAllocated()) {
		logger.log(2, "Proposal skipped: Acc.Total="+total);
		logger.log(2, "Proposal skipped: U="+total.getUsed()+" A="+total.getAllocated());
		logger.log(2, "Proposal skipped: PROP_TIME_ALLOC_EXCEEDED");
		continue;
	    }
	
	    Iterator iGroup = proposal.listAllGroups();		
	    while (iGroup.hasNext()) {
		
		Group group = (Group)iGroup.next();
	
		ExecutionStatistics hist = historyModel.getExecutionStatistics(group);
		
		if (execModel.canDo(group, time, env, hist)) {
		    // TODO scoringmodel should generate the metrics itsself..
		    double score = scoringModel.scoreGroup(group, acc, time, env, hist);
		    Metric metric = new Metric(group, score);
		    metric.accounts = acc;
		    metric.generator = "BSM";

		    // now work out how much time is left for this group to exec in current window...
		    //long ttx = rtCalculator.calculateRemainingTime(group, time);
		    //metric.remaining = ttx;

		    logger.log(2, "Adding group: "+metric);
		    candidates.add(metric);
		    
		}
		
	    } // next group

	} // next proposal

	return candidates;

    }

}
